CPP|变量、对象、对象成员初始化的一些细节

变量初始化是指变量定义后的第一次赋值,此后的赋值一般称为数据的更新或修改。

我们知道,世间万物都可以数据化表示,进一步可以比特化表示,也就是用一串串0100110010101001……来表示,而0、1的硬件实现就是一个用作开关的晶体管。

图片描述(最多50字)

每一个0或1称为一个比特(bit,对应一个开关晶体管),8个比特是一个字节(byte,),称为一个内存单元(所以字节是最小的内存单位),内存单元在内存中线性排列,可以随机访问。在C++中,一个字母就用一个字节来表示,数字可以用4或8个字节来表示。通过数据类型的定义和类型的声明,编译器知道需要从哪个内存单元(变量名对应内存地址)开始访问,访问到哪个位置(内存单元)结束(C中没有定义字符串,以'\0'标志结尾,文件以EOF标识)。

即使变量没有初始化,变量对应的地址值仍然对应一个内存单元,对应一串比特,这一串比特的值是不确定的、随机的,可以称为垃圾值。这些垃圾值不是程序员所期望的,所以未初始化的变量使用(在右值中引用)时会有不可预料的错误,所以编译器对部分数据类型有强制化要求初始化或默认初始化的行为。

对于指针,如果未初始化,其随机值不确定,也就是需要指向的内存单元不确定,更会引发不可预料的错误。

1 未初始化的变量在右值引用时会出现错误

int j; //此时未初始化,j为一随机值或垃圾值

int i = 0;

i = j+1; //未初始化的在右值中引用会引发错误

cout<<i<<endl; //输出-858993459

如果是指针未初始化直接引用,程序会直接崩掉,因为指向找不到内存单元地址。

2 强制要求初始化的部分

2.1 引用要求声明时必须同时初始化;

2.2 const常量(基本类型变量或对象)也必须在声明时同时初始化(对const对象只能调用const成员函数);

3 建议初始化的部分

指针建议声明时初始化,或最近位置初始化;

4 编译器会自动初始化的部分

编译器会自动初始化未初始化的全局或静态变量;

5 局部变量为什么编译器不自动初始化

效率的考量,因为变量的赋值也是需要时间的;

6 数组的初始化

声明数组的同时可以对数组初始化:

float x[5] = { -1.1, 0.2, 33.0, 4.4, 5.05 };

初始化表的长度短于要被初始化的数组元素数目,那么剩余元素被初始化为0。

带有初始化的数组可以不定义长度:

int a[]={1,2,3,4,5}; //则默认数组大小为5

初始化可以使用上述的集合赋值,但初始化后需要再次修改时却不能再次使用这种集合操作。因为你无法再次用x做右值,因为x相当于一个指针常量,你如果使用x[i]做左值,此时的x[i]只是其中的一个元素而已:

float fn=1.1;

float x[5] = { -1.1, 0.2, 33.0, 4.4, 5.05 };

float y[] ={1.0,2,3,4,5};

//x = y; //错误,因为x不能做左值,它相当于一个指针变量

x[5] = 3.3; //溢出错误,因为元素序列从0开始,只有5-1个元素,

cout<<fn<<endl; //输出3.3,因为此时的x[5]的内存单元地址对应的是fn的内存地址,也就是x[5]与fn指向了同一内存单元,且因为都是float,也就是访问的内存单元长度相同,;所以可以被编译器解析,其值就是更新后的3.3。

7 对象的初始化

对象的初始化使用一种特殊的成员函数,也就是构造(constructor)函数,通过构造函数为对象分配空间,进行初始化。

当程序员没有定义并实现构造函数时,编译器会调用一个默认的构造函数。

类的设计者在定义一个类时如果没有定义任何成员函数,也会有默认的四个函数:默认的构造函数、默认的复制构造函数、析构函数和赋值运算符重载函数。

构造函数可以重载,让对象有多种初始化形式。

当数据成员不是普通的内置类型,而是某一个类的对象(数据成员是类对象的情形成为数据聚合),可能无法直接用赋值语句在构造函数体中为它赋初值,程序员要显式声明并定义一个构造函数。

构造函数还有一个与普通函数不同的地方,就是可以包含一个构造函数初始化列表。

构造函数初始化列表位于函数头和函数体之间。它以一个冒号开头,接着是一个以逗号分隔的数据成员列表。

每个数据成员的后面跟着一个放在圆括号中的对应于该数据成员的构造函数的实际参数表。

如IntArray的构造函数可写为

IntArray :: IntArray(int lh, int rh): low(lh), high(rh)//low、high是成员,lh、rh是对应的值

{ storage = new int [high - low + 1]; }

显然利用初始化列表可以提高构造函数的效率。在初始化的时候,同时完成了赋初始的工作。

有两种情况必须用初始化列表。第一种情况是数据成员中含有一些不能用赋值操作进行赋值的数据成员,例如常量数据成员或对象数据成员,这时必须在初始化列表中调用数据成员所属类型的构造函数来构造它们。第二种情况是在用派生的方法定义一个类时,派生类对象中的基类部分必须在构造函数的初始化列表中调用基类的构造函数完成。

8 不能在类声明中初始化const数据成员

const数据成员的初始化只能在类构造函数的初始化表中进行,不能在构造函数中对他赋值。

常量的数据成员指得是那些在对象生成时给定了初值,在整个对象的生命周期中,该数据成员的值是不能变的。常量数据成员的值必须在构造函数的初始化列表中进行初始化。

class A

{

A(int size); //构造函数

const int SIZE;

}

A::A(int size) : SIZE(size) //构造函数的初始化表

{…}

A a(100); //对象a的SIZE的值为100

A b(200); //对象b的SIZE的值为200

9 不能在类的构造函数中声明静态成员数据

静态成员变量的初始化不能放在类的构造函数中。

为静态成员分配空间称为静态成员的定义,静态成员的定义一般出现在类的实现文件中。如在SavingAccount类的实现文件中,必须要如下的定义:

double SavingAccount::rate = 0.05;

该定义为rate分配了空间,并给它赋了一个初值0.05。

如果没有这个定义,连接器会报告一个错误。

静态成员数据只能用静态成员函数来访问。

静态的常量数据成员是整个类所有对象共享的一个常量。对整个类而言,不管定义了多少个对象,该成员永远只有一份拷贝。静态常量数据成员的值是在定义类时给定。

-End-

本页共70段,2814个字符,6950 Byte(字节)